/**
 * @fileOverview
 */

/**
 * @namespace & top-level @class
 * @requires jQuery, underscore.js
 */
var Karamu = Karamu || function(view){
    this.view = view;

    this.services = {
        guid:       '/swift/services/guid/runMashup',
        categories: '/swift/services/categories/runMashup',
        gadgets:    '/swift/services/gadget-store/runMashup',
        gadget:     '/infinispan-server-rest/rest/gadgets/%data%',
        groups:     '/infinispan-server-rest/rest/groups/%data%',
        group:      '/infinispan-server-rest/rest/group/%data%'
    };

    /**
     * Instantiates a namespaced sub-object as a controller; capitalization is used to differntiate between protoyypes
     * and instantiated controller objects. The Karamu object is passed as the the context so controllers can access
     * top-level methods and other controllers.
     */
    this.addController = function(controller){
        if (typeof this.controllers !== 'object') {
            this.controllers = {};
        }
        if (_.isString(controller) && controller.length > 0) {
            var Controller = controller.charAt(0).toUpperCase() + controller.slice(1);
            if (Karamu.Controllers[Controller]) {
                this.controllers[controller] = new Karamu.Controllers[Controller](this);
            }
        }
    };

    /**
     * An extremely "lightweight" user authentication scheme. Replace with something real.
     */
    this.userId = undefined;

    this.authenticate = function(args){
        $.cookie.json = true;
        var value = {
            authenticated: 'true',
            username: args.username,
            userid: 'anonymous'
        };
        this.getGUID({
            context: this,
            success: function(xml){
                value.userid = $('guid', xml).text();
            },
            error: function(){
                if (console && console.log) console.log('Error retrieving GUID.');
            },
            complete: function(){
                if (typeof args.expires === 'number') {
                    $.cookie('KARAMU', value, { expires: args.expires });
                } else {
                    $.cookie('KARAMU', value);
                }
                if (typeof args.callback === 'function') {
                    args.callback.call();
                }
            }
        });
    };

    this.authenticated = function(){
        $.cookie.json = true;
        var cookie = $.cookie('KARAMU');
        if (typeof cookie !== 'undefined') {
            this.userId = cookie.userid;
            return true;
        } else {
            return false;
        }
    };

    this.cannonizeUrl = function(url){
        if (!url) {
            return '';
        }
        url = url.toLowerCase();
        var loc = window.location,
            hostport = '';
        if ((loc.protocol === 'http' && loc.port !== '80') || (loc.protocol === 'https' && loc.port !== '443')) {
            hostport = loc.protocol + '//' + loc.hostname;
        } else {
            hostport = loc.protocol + '//' + loc.host;
        }

        if (url.indexOf('//') == 0) {
            return loc.protocol + url;
        }

        if (url.indexOf('/') == 0) {
            return hostport + url;
        } else {
            return hostport + loc.pathname.substr(0, loc.pathname.lastIndexOf('/')) + '/' + url;
        }
    };

    /**
     * @param {(Object|string)} el a jQuery object or jQuery selector for a single object
     * @param {?Object} data if omitted, an empty object is used to trigger the template substitution
     * @public
     */
    this.tmpl = function(el, data){
        if (_.isUndefined(data)) data = {};
        return $($.trim(_.template($('#template-' + el).html(), data)));
    };

    /**
     * Get a new GUID
     *
     * @param {?Function} args.complete
     * @param {Object} args.context the top-level controller object, e.g karamu; alternatively, a plain object with a
     *                              defined services.guid property
     * @param {?Function} args.error
     * @param {?Function} args.success
     * @access public
     */
    this.getGUID = function(args){
        $.ajax({
            cache: false,
            complete: args.complete,
            context: args.context,
            dataType: 'xml',
            error: args.error,
            success: args.success,
            type: 'GET',
            url: args.context.services.guid
        });
    };

    /**
     * @param {string} view e.g. 'desktop' or 'dashboard'
     * @public
     */
    this.observe = function(){
        $(this.models[this.view]).on({
            'change-model': function(e){
                // If a complete refresh is needed, one could call e.data.render() at this point...
            }
        }, this);

        if (this.controllers.groups) {
            $(this.models[this.view]).on({
                'change-groups': this.controllers.groups.change
            }, this);
        }

        if (this.controllers.group) {
            $(this.models[this.view]).on({
                'add-group': this.controllers.group.add,
                'change-group': this.controllers.group.change,
                'remove-group': this.controllers.group.remove
            }, this);
        }

        if (this.controllers.gadget) {
            $(this.models[this.view]).on({
                'add-gadget': this.controllers.gadget.add,
                'change-gadget': this.controllers.gadget.change,
                'remove-gadget': this.controllers.gadget.remove
            }, this);
        }

        if (this.controllers.shortcut) {
            $(this.models[this.view]).on({
                'add-shortcut': this.controllers.shortcut.add,
                'change-shortcut': this.controllers.shortcut.change,
                'remove-shortcut': this.controllers.shortcut.remove
            }, this);
        }
    };

    this.fetchModel = function(){
        this.controllers.groups.fetch({
            context: this,
            view: this.view,
            userId: this.userId,
            success: function(groupsData){
                if (_.isArray(groupsData) && groupsData.length > 0) {
                    this.models[this.view].groups = groupsData;
                    var groupsFetched = 0;
                    _.each(this.models[this.view].groups, function(group, groupIndex){
                        this.controllers.group.fetch({
                            context: this,
                            groupId: group.id,
                            success: function(groupData){
                                _.each(this.models[this.view].groups, function(group2, groupIndex2){
                                    if (group2.id === groupData.id) {
                                        if (!_.isEqual(group2, groupData)) {
                                            this.models[this.view].groups[groupIndex2] = groupData;
                                            //$(this.models[this.view]).trigger('change-model');
                                        }
                                    }
                                }, this);
                            },
                            error: function(){
                                if (console && console.log) console.log('Error retrieving group ' + group.id + '.');
                            },
                            complete: function(){
                                groupsFetched++;
                                if (groupsFetched === this.models[this.view].groups.length) {
                                    this.render();
                                }
                            }
                        });
                    }, this);
                } else {
                    // Groups data isn't missing, it's just empty.
                    switch (this.view) {
                        case 'dashboard':
                            // No default groups
                            break;
                        case 'desktop':
                            this.controllers.group.addNamedGroup('Favorites', this);
                            break;
                    }
                    this.render();
                }
            },
            error: function(xhr, status){
                // Groups data is missing entirely.
                if (xhr.status === 404 || xhr.status === 200) {
                    this.models[this.view].groups = [];
                    this.controllers.groups.push({
                        context: this,
                        view: this.view,
                        userId: this.userId,
                        data: [],
                        success: function(){
                            switch (this.view) {
                                case 'dashboard':
                                    // No default groups
                                    break;
                                case 'desktop':
                                    this.controllers.group.addNamedGroup('Favorites', this);
                                    break;
                            }
                            this.render();
                        }
                    });
                }
            }
        });
    };

    this.render = function(){
        switch (this.view) {
            case 'dashboard':
                if (gadgets && gadgets.rpc) {
//                    gadgets.rpc.register('resize_iframe', function(height) {
//                        var gadget = $('#' + this.f).parents('.gadget');
//                        if (gadget) {
//                            $('.gadget-body', gadget).height(height);
//                            $('.gadgets-gadget-content', gadget).height(height);
//                            //$('.gadgets-gadget-content iframe', gadget).height(height);
//                        }
//                    });
                    gadgets.rpc.register('setTitle', function(title) {
                        var gadget = $('#' + this.f).parents('.gadget');
                        $('#' + this.f + ' .title').text(title);
                    });
                }
                if (gadgets && gadgets.pubsub2router) {
                    gadgets.pubsub2router.hub.subscribe('adjustHeight', function(topic, data){
                        var gadget = $(data.gadget);
                        if (gadget) {
                            $('.gadget-body', gadget).height(data.height);
                            $('.gadgets-gadget-content', gadget).height(data.height - 18);
                            $('.gadgets-gadget-content iframe', gadget).height(data.height - 18);
                        }
                    });
                }

                var gS = '.gadget',
                    dS = '.tab-pane.active .tab-pane-column';

                this.controllers.catalog.render('#dashboard-gadget-store', {
                    gadgetSelector: gS,
                    dropzoneSelector: dS,
                }, this);

                this.controllers.groups.render('#dashboard-tabs', {
                    gadgetSelector: gS,
                    dropzoneSelector: dS,
                    groups: this.models[this.view].groups
                }, this);

                // Remove any handlers added on a previous render, else they'll fire multiple times
                $('a[href="#newTab"]').off().on({
                    click: this.controllers.group.addNewTab
                }, this);
                $('a[href="#layout"]').off().on({
                    click: this.controllers.group.changeLayout
                }, this);

                break;
            case 'desktop':
                if (gadgets && gadgets.rpc) {
//                    gadgets.rpc.register('resize_iframe', function(height) {
//                        var gadget = $('#' + this.f).parents('.gadget');
//                        if (gadget) {
//                            $('.gadget-body', gadget).height(height);
//                            $('.gadget-dimmer', gadget).height(height);
//                            $('.gadgets-gadget-content', gadget).height(height);
//                            //$('.gadgets-gadget-content iframe', gadget).height(height);
//                        }
//                    });
                    gadgets.rpc.register('setTitle', function(title) {
                        var gadget = $('#' + this.f).parents('.gadget');
                        $('#' + this.f + ' .title').text(title);
                    });
                }
                if (gadgets && gadgets.pubsub2router) {
                    gadgets.pubsub2router.hub.subscribe('adjustHeight', function(topic, data){
                        var gadget = $(data.gadget);
                        if (gadget) {
                            $('.gadget-body', gadget).height(data.height);
                            $('.gadget-dimmer', gadget).height(data.height);
                            $('.gadgets-gadget-content', gadget).height(data.height - 18);
                            $('.gadgets-gadget-content iframe', gadget).height(data.height - 18);
                        }
                    });
                }

                var gS = '.gadget-shortcut',
                    dS = '.group .gadgets';

                this.controllers.catalog.render('#desktop-gadget-store', {
                    gadgetSelector: gS,
                    dropzoneSelector: dS,
                }, this);

                this.controllers.desktop.renderGroups('#groups', this.models[this.view].groups);
                this.controllers.desktop.renderSidebar('#sidebar', _.where(this.models[this.view].groups, {title: 'Favorites'})[0]);

                // Remove any handlers added on a previous render, else they'll fire multiple times
                $('#start-menu').off().on({
                    click: this.controllers.desktop.startMenu
                }, this);
                $('#add-group').off().on({
                    click: this.controllers.desktop.addGroup
                }, this);
                $('#search').off().on({
                    keyup: this.controllers.desktop.search
                }, this);

                break;
        }
    };

};